function PhotoPuzzleView(jqTableContainer, width, height, aspectRatio) {
	this.aspectRatio = aspectRatio;
	this.jqTableContainer = jqTableContainer;
	this.height = height;
	this.width = width;
	this.enabled = true;
}

PhotoPuzzleView.prototype.init = function(controller) {
	this.controller = controller;
	this.selected = -1;
	this.initTable();

	var self = this;
	$(window).resize(function() {
		self.updateDimensions();
	});
	this.updateDimensions();
};

PhotoPuzzleView.prototype.initTable = function() {
	this.cells = [];
	var table = $('<table class="photo-table">');

	var cellIndex = 0;
	for ( var row = 0; row < this.height; row++) {
		var tr = $('<tr>');
		table.append(tr);
		for ( var col = 0; col < this.width; col++) {
			var td = $('<td>');
			tr.append(td);
			this.cells.push(td);
			td.click(this.createCellClickHandler(cellIndex));
			cellIndex++;
		}
	}

	this.jqTable = table;
	this.jqTableContainer.append(this.jqTable);
};

PhotoPuzzleView.prototype.createCellClickHandler = function(cellIndex) {
	var _this = this;
	return function() {
		if (!_this.enabled) {
			return;
		}

		if (_this.selected >= 0) {
			_this.controller.onSwap(cellIndex);
		} else {
			_this.controller.onSelect(cellIndex);
		}
	};
};

PhotoPuzzleView.prototype.updateDimensions = function() {
	var bkgW = this.jqTable.width();
	var bkgH = this.jqTable.height();
	var cellH = (bkgW / this.aspectRatio) / this.height;

	for ( var i = 0; i < this.width * this.height; i++) {
		var cell = this.getCell(i);

		// background-size is modified to match with relative bkg position (see update(...))
		cell.css('background-size', bkgW + 'px ' + bkgH + 'px');

		// cell height is modified to keep original image aspect ratio. This cannot be done through CSS (
		// perhaps with vmin, but it doesn't work very well)
		cell.css('height', cellH + "px");
	}
};

PhotoPuzzleView.prototype.update = function(state) {
	this.updateDimensions();
	this.updateCellOffsets(state.positions);
	this.updateSelection(state.selected);
};

PhotoPuzzleView.prototype.updateCellOffsets = function(positions) {
	for ( var i = 0; i < positions.length; i++) {
		var col = (positions[i] - 1) % this.width;
		var row = Math.floor((positions[i] - 1) / this.width);

		var cell = this.getCell(i);
		var tdW = col / (this.width - 1) * 100;
		var tdH = row / (this.height - 1) * 100;
		cell.css('background-position', tdW + '% ' + tdH + '%');
	}
};

PhotoPuzzleView.prototype.updateSelection = function(selected) {
	if (this.selected >= 0) {
		this.getCell(this.selected).removeClass('selected');
	}
	this.selected = selected;
	if (selected >= 0) {
		this.getCell(this.selected).addClass('selected');
	}
};

PhotoPuzzleView.prototype.getCell = function(i) {
	return this.cells[i];
};

PhotoPuzzleView.prototype.onSolved = function() {
	alert('Solved!');
	this.setEnabled(false);
};

PhotoPuzzleView.prototype.setEnabled = function(enabled) {
	this.enabled = enabled;
};